﻿using System;
using System.Collections.Generic;

namespace Borg
{
    [Slot("Application", AutoOpen = false)]
    [Slot("Discoverer", AutoOpen = false)]
    [Extension]
    public class Startup : ExtensionBase, IDisposable
    {
        private ITypeStoreModifier _typeStoreModifier;

        private bool _wasApplicationPlugged;
        private bool _isAutoShutdownEnabled;

        protected Slot ApplicationSlot
        {
            get { return Slots["Application"]; }
        }

        protected Slot DiscovererSlot
        {
            get { return Slots["Discoverer"]; }
        }

        protected internal virtual void Start(ITypeStoreModifier typeStoreModifier)
        {
            if (_typeStoreModifier != null)
                throw new InvalidOperationException("Startup extension is already started");
            
            if (typeStoreModifier == null)
                throw new ArgumentNullException("typeStoreModifier");
            
            _typeStoreModifier = typeStoreModifier;

            DiscovererSlot.Plugged += AddDiscovererListeners;
            DiscovererSlot.Unplugging += RemoveDiscovererListeners;
            DiscovererSlot.Unplugged += CheckShutdown;
            DiscovererSlot.Open();
            
            ApplicationSlot.Plugged += StartApplication;
            ApplicationSlot.Unplugged += CheckShutdown;
            ApplicationSlot.Open();
            
            EnableAutoShutdown();
        }

        public virtual void Dispose()
        {
            DiscovererSlot.Plugged -= AddDiscovererListeners;
            DiscovererSlot.Unplugging -= RemoveDiscovererListeners;
            DiscovererSlot.Unplugged -= CheckShutdown;
            
            ApplicationSlot.Plugged -= StartApplication;
            ApplicationSlot.Unplugged -= CheckShutdown;
        }

        private void StartApplication(CompositionEventArgs args)
        {
            _wasApplicationPlugged = true;
            ((IApplication)args.Plug.Extension.Object).Start();
        }

        private void AddDiscovererListeners(CompositionEventArgs args)
        {
            var discoverer = (IDiscoverer)args.Plug.Extension.Object;

            discoverer.Added += AddContractsAndPlugins;
            discoverer.Removed += RemoveContractsAndPlugins;

            discoverer.Start(_typeStoreModifier.TypeBuilder);
        }

        private void RemoveDiscovererListeners(CompositionEventArgs args)
        {
            var discoverer = (IDiscoverer)args.Plug.Extension.Object;

            discoverer.Added -= AddContractsAndPlugins;
            discoverer.Removed -= RemoveContractsAndPlugins;
            
            CheckShutdown();
        }

        private void CheckShutdown(CompositionEventArgs args)
        {
            CheckShutdown();
        }

        private void AddContractsAndPlugins(DiscoveryEventArgs args)
        {
            var contracts = new List<Contract>(args.Contracts.Count);
            LogArgs log;

            foreach (var contract in args.Contracts)
            {
                if (_typeStoreModifier.CanAdd(contract, contracts, out log))
                    contracts.Add(contract);
                else Logger.Log(VerbosityLevel.Minimal, log);
            }

            var plugins = new List<Plugin>(args.Plugins.Count);

            foreach (var plugin in args.Plugins)
            {
                if (_typeStoreModifier.CanAdd(plugin, plugins, out log))
                    plugins.Add(plugin);
                else
                    Logger.Log(VerbosityLevel.Minimal, log);
            }
            
            _typeStoreModifier.Add(contracts, plugins);
        }

        private void RemoveContractsAndPlugins(DiscoveryEventArgs args)
        {
            _typeStoreModifier.Remove(args.Contracts, args.Plugins);
        }

        private void EnableAutoShutdown()
        {
            _isAutoShutdownEnabled = true;
            CheckShutdown();
        }

        private void CheckShutdown()
        {
            if (!_isAutoShutdownEnabled ||
                Runtime.State == RuntimeState.ShutdownStarted ||
                (Runtime.State == RuntimeState.Terminated ||
                Slots["Discoverer"].PluggedPlugs.Count > 0) ||
                Slots["Application"].PluggedPlugs.Count > 0)
                return;

            if (!_wasApplicationPlugged)
                Logger.Log(
                    LogType.Error,
                    "The system has no extensions which contribute to the" +
                    " slots 'Application' or 'Discoverer' of extension 'Startup'",
                    new object[0]);
            
            Runtime.BeginShutdown();
        }
    }
}
